
/****************************************************************************/
/*Copyright (c) 2011, Florent DEVILLE.                                      */
/*All rights reserved.                                                      */
/*                                                                          */
/*Redistribution and use in source and binary forms, with or without        */
/*modification, are permitted provided that the following conditions        */
/*are met:                                                                  */
/*                                                                          */
/* - Redistributions of source code must retain the above copyright         */
/*notice, this list of conditions and the following disclaimer.             */
/* - Redistributions in binary form must reproduce the above                */
/*copyright notice, this list of conditions and the following               */
/*disclaimer in the documentation and/or other materials provided           */
/*with the distribution.                                                    */
/* - The names of its contributors cannot be used to endorse or promote     */
/*products derived from this software without specific prior written        */
/*permission.                                                               */
/* - The source code cannot be used for commercial purposes without         */ 
/*its contributors' permission.                                             */
/*                                                                          */
/*THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       */
/*"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         */
/*LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS         */
/*FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE            */
/*COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,       */
/*INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,      */
/*BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;          */
/*LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER          */
/*CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT        */
/*LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN         */
/*ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
/*POSSIBILITY OF SUCH DAMAGE.                                               */
/****************************************************************************/

#include "CameraEye.h"

#include "CameraAt.h"
#include "CameraUp.h"

#include "CMatrix.h"
#include <QGLWidget>
#include "OGLVisu.h"


//constructor
CameraEye::CameraEye(CameraUp* up, CameraAt* at) : ICamera(CT_EYE), _atElement(at), _upElement(up), _at(0, 1, 0)
	, _up(0, 1, 0)
{
	_translate = CVector3f(0, 0, 0);
	_movableType = MT_CAMERA;

	_translateMark = CVector3f(0, 1.f, 0.5f);
	_rotationMarkRadius = 2.f;
}

//destructor
CameraEye::~CameraEye(){}

//pre render the camera
void CameraEye::preRender()
{
	//calculate the world matrix
	_at = _atElement->getTranslate() - _translate;
	_at.normalize();
	_up = _upElement->getTranslate() - _translate;
	_up.normalize();

	CMatrix m;
	m.createFrenet(_translate, _at, _up);

	m.getOpenGLMatrix(_lookAtMatrix);

	glPushMatrix();

	glMultMatrixd(_lookAtMatrix);
}

//render the camera
void CameraEye::Render()
{
	VISU.drawCamera();
	//VISU.drawBox(3, 1, 1);

	//if the camera is selected, draw the surrounding box and the translation mark
	if(_selected)
	{
		glPushMatrix();
		glTranslatef(0, 0, 0.5f);
		VISU.drawSelectionBox(2, 2, 1.5f);
		glPopMatrix();
	}

	glPopMatrix();

	//draw the two lines, at and up
	glDisable(GL_LIGHTING);
	glColor3f(1, 1, 1);
	CVector3f atPoint = _atElement->getTranslate() - _translate;
	glBegin(GL_LINES);
		glVertex3f(_translate.x, _translate.y, _translate.z);
		glVertex3f(_atElement->getTranslate().x, _atElement->getTranslate().y,
			_atElement->getTranslate().z);

		glVertex3f(_translate.x, _translate.y, _translate.z);
		glVertex3f(_upElement->getTranslate().x, _upElement->getTranslate().y,
			_upElement->getTranslate().z);
	glEnd();
	glEnable(GL_LIGHTING);
}

//post render for the camera
void CameraEye::postRender()
{
	//glPopMatrix();
}

//set the translation vector
void CameraEye::setTranslate(const CVector3f& t)
{
	_translate = t;
}

//set the rotation angles
void CameraEye::setRotation(const CVector3f& r)
{}

//set the scaling factors
void CameraEye::setScale(const CVector3f& s){}

//translate the object's position by the vector
void CameraEye::translate(const CVector3f& t)
{
	_translate = _translate + t;
	//_atElement->translate(t);
	_upElement->translate(t);
}

//rotate the objects using the vector
void CameraEye::rotate(const CVector3f& r)
{}

//add the vector as scale components
void CameraEye::scale(const CVector3f& s){}

//determine if the point v is a point of the geometry object
bool CameraEye::isGeometry(const CVector3f& v) const
{
	//calculate world matrix
	//CVector3f dir = _atElement->getTranslate() - _translate;
	//dir.normalize();
	//CVector3f s_up = _upElement->getTranslate() - _translate;
	//s_up.normalize();

	CMatrix transform;
	transform.createFrenet(_translate, _at, _up);


	//get V in local coordinate of the sphere
	CVector3f localV = v * transform.inverse();

	//test if inside
	float error = 0.1f;
	float halfWidth = 1 + error;
	float halfHeight = 1 + error;
	float halfDepth = 1 + error;
	if (localV.x >= -halfWidth && localV.x <= halfWidth &&
		localV.y >= -halfHeight && localV.y <= halfHeight &&
		localV.z >= -error && localV.z <= halfDepth)
		return true;

	return false;
}

//test if the point v is on the mark of the primitive
//out indicate which axis is hoovered
bool CameraEye::isTranslationMark(const CVector3f& v)
{
	//calculate the world matrix of the primitive	
	/*CVector3f dir = _atElement->getTranslate() - _translate;
	dir.normalize();
	CVector3f s_up = _upElement->getTranslate() - _translate;
	s_up.normalize();*/

	CMatrix transform;
	transform.createFrenet(_translate, _at, _up);

	CMatrix t;
	t.createTranslation(_translateMark);
	transform = t * transform;

	_hoover.x = 0;
	_hoover.y = 0;
	_hoover.z = 0;

	//get the point in the local coordinates of the translation mark
	CVector3f distance = v * transform.inverse();

	//get and test the distance between the point and the origin of the translation mark
	//CVector3f distance = v - center;
	if(distance.norme2() > 1)
		return false;

	distance.normalize();

	const float treshold = 0.8f;

	//text with the tree axis.
	CVector3f x(1, 0, 0);
	if(x.dot(distance) > treshold)
	{
		_hoover.x = 1;
		return true;
	}

	CVector3f y(0, 1, 0);
	float dot = y.dot(distance);

	if(y.dot(distance) > treshold)
	{
		_hoover.y = 1;
		return true;
	}
	CVector3f z(0, 0, 1);
	if(z.dot(distance) > treshold)
	{
		_hoover.z = 1;
		return true;
	}

	//v is not the mark
	return false;
}

//get the rotation matrix of the movable object
CMatrix CameraEye::getRotationMatrix()const
{
	//calculate front and up
	/*CVector3f dir = _atElement->getTranslate() - _translate;
	dir.normalize();
	CVector3f s_up = _upElement->getTranslate() - _translate;
	s_up.normalize();*/

	//calculate frenet matrix
	CMatrix transform;
	transform.createFrenet(CVector3f(0, 0, 0), _at, _up);

	return transform;
}

//get the at vector of the camera
CVector3f CameraEye::getAt()const
{
	return _atElement->getTranslate();
}

//get the up vector of the camera
CVector3f CameraEye::getUp()const
{
	return _up;
}

//get the at element
CameraAt* CameraEye::getAtElement()
{
	return _atElement;
}

//get the up element
CameraUp* CameraEye::getUpElement()
{
	return _upElement;
}

//get the eye in the raytracer coordinate space
CVector3f CameraEye::getRTEye()const
{
	return CVector3f(_translate.x, _translate.y, -_translate.z);
}

//get the at in the raytracer coordinate space
CVector3f CameraEye::getRTAt()const
{
	CVector3f at = getAt();
	at.z *= -1;
	return at;
}

//get the up vector in the raytracer coordinate space
CVector3f CameraEye::getRTUp()const
{
	CVector3f up = getUp();
	up.z *= -1;
	return up;
}

//set the at element
void CameraEye::setAtElement(CameraAt* at)
{
	_atElement = at;
}

//set the up element
void CameraEye::setUpElement(CameraUp* up)
{
	_upElement = up;
}